package com.gzhryc.jdbc;

import com.gzhryc.jdbc.annotaion.*;
import com.gzhryc.jdbc.log.Logger;
import com.gzhryc.jdbc.models.JoinEntity;
import com.gzhryc.jdbc.utils.ClassTools;
import com.gzhryc.jdbc.utils.EntityUtils;
import com.gzhryc.jdbc.utils.HelperUtils;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Field;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

public class JoinSelectDao<T> {

	protected List<JoinEntity> joinClassList;

	protected JdbcHelper jdbcHelper;

	protected final Class<T> clazz;
	protected String tableName;
	protected Field idField;

	@SuppressWarnings("unchecked")
	public JoinSelectDao(String jdbcKey) {
		this.clazz = (Class<T>) EntityUtils.getParameterizedType(this);
		this.tableName = EntityUtils.getName(this.clazz); // 支持表和视图
		this.idField = EntityUtils.getIdField(this.clazz);
		this.jdbcHelper = HelperUtils.createJdbcHelper(jdbcKey);
		analysisJoin();
	}

	public T getJoinById(Object id) throws SQLException {
		if (id != null && idField != null) {
			String idName = EntityUtils.getName(idField);
			String[] temp = buildJoinSql();
			String sql = "SELECT " + temp[0] + " FROM " + tableName + " t" + temp[1] + " WHERE t." + idName + "=?";
			Query query = new Query(sql);
			query.getParams().setObject(id);
			return this.jdbcHelper.getJoin(query, clazz);
		} else {
			throw new SQLException("id is null");
		}
	}

	public List<T> findJoinAll() throws SQLException {
		String[] temp = buildJoinSql();
		String sql = "SELECT " + temp[0] + " FROM " + tableName + " t " + temp[1];
		return this.jdbcHelper.findJoin(sql, clazz);
	}

	public List<T> findJoinAll(Integer pageNo, Integer pageLimit) throws SQLException {
		String[] temp = buildJoinSql();
		String sql = "SELECT " + temp[0] + " FROM " + tableName + " t " + temp[1];
		Query query = new Query(sql);
		query.setPageNo(pageNo);
		query.setPageLimit(pageLimit);
		return this.jdbcHelper.findJoin(query, clazz);
	}

	public List<T> findJoinByCondition(Conditions condition) throws SQLException {
		String[] temp = buildJoinSql();
		String sql = "SELECT " + temp[0] + " FROM " + tableName + " t " + temp[1];
		Query query = new Query(sql);
		query.appendSql(condition.toJoinSql(query.getParams(), "t", true));
		return this.jdbcHelper.findJoin(query, clazz);
	}

	public List<T> findJoinByCondition(Conditions condition, Integer pageNo, Integer pageLimit) throws SQLException {
		String[] temp = buildJoinSql();
		String sql = "SELECT " + temp[0] + " FROM " + tableName + " t " + temp[1];
		Query query = new Query(sql);
		query.appendSql(condition.toJoinSql(query.getParams(), "t", true));
		query.setPageNo(pageNo);
		query.setPageLimit(pageLimit);
		return this.jdbcHelper.findJoin(query, clazz);
	}

	public Long countJoin() throws SQLException {
		String[] temp = buildJoinSql();
		String sql = "SELECT count(1) as TOTAL FROM " + tableName + " t " + temp[1];
		Map<String, Object> result = this.jdbcHelper.get(sql);
		return Long.valueOf(result.get("TOTAL").toString());
	}

	public Long countJoin(Conditions condition) throws SQLException {
		String[] temp = buildJoinSql();
		String sql = "SELECT count(1) as TOTAL FROM " + tableName + " t " + temp[1];
		Query query = new Query(sql);
		query.appendSql(condition.toJoinSql(query.getParams(), "t", true));
		Map<String, Object> result = this.jdbcHelper.get(query);
		return Long.valueOf(result.get("TOTAL").toString());
	}

	public T getJoin(Conditions condition) throws SQLException {
		String[] temp = buildJoinSql();
		String sql = "SELECT " + temp[0] + " FROM " + tableName + " t " + temp[1];
		Query query = new Query(sql);
		query.appendSql(condition.toJoinSql(query.getParams(), "t", true));
		return this.jdbcHelper.getJoin(query, clazz);
	}

	/**
	 * 解析表连接对象
	 */
	protected void analysisJoin() {
		Field[] fields = ClassTools.getDeclaredFields(this.clazz);
		for (Field field : fields) {
			Join join = field.getAnnotation(Join.class);
			if (join != null) {
				if (joinClassList == null) {
					joinClassList = new ArrayList<JoinEntity>();
				}
				String tableName = join.toTable();
				if (StringUtils.isBlank(tableName)) {
					tableName = EntityUtils.getTableName(field.getType());
				}
				JoinEntity joinEntity = new JoinEntity();
				joinEntity.setFieldName(field.getName());
				joinEntity.setFromField(join.fromField());
				joinEntity.setType(join.type().toUpperCase());
				joinEntity.setClazz(field.getType());
				joinEntity.setToTable(tableName);
				joinEntity.setToField(join.toField());
				joinEntity.setToTableAlias(field.getName());
				joinEntity.setPriority(join.priority());
				joinClassList.add(joinEntity);
			} else {
				JoinField joinField = field.getAnnotation(JoinField.class);
				if (joinField != null) {
					if (joinClassList == null) {
						joinClassList = new ArrayList<JoinEntity>();
					}
					String tableName = joinField.toTable();
					if (StringUtils.isNotBlank(tableName)) {
						JoinEntity joinEntity = new JoinEntity();
						joinEntity.setFieldName(field.getName());
						joinEntity.setName(joinField.name());
						joinEntity.setFromField(joinField.fromField());
						joinEntity.setType(joinField.type().toUpperCase());
						joinEntity.setClazz(this.clazz);
						joinEntity.setToTable(tableName);
						joinEntity.setToField(joinField.toField());
						joinEntity.setToTableAlias(joinField.toTableAlias());
						if (StringUtils.isBlank(joinEntity.getToTableAlias())) {
							joinEntity.setToTableAlias(tableName);
						}
						joinEntity.setPriority(joinField.priority());
						joinClassList.add(joinEntity);
					}
				} else {
					Parent parent = field.getAnnotation(Parent.class);
					if (parent != null) {
						if (joinClassList == null) {
							joinClassList = new ArrayList<JoinEntity>();
						}
						JoinEntity joinEntity = new JoinEntity();
						joinEntity.setFieldName(field.getName());
						joinEntity.setName(parent.value());
						joinEntity.setFromField(new String[] { "parent_id" });
						joinEntity.setType("LEFT");
						joinEntity.setClazz(this.clazz);
						joinEntity.setToTable(tableName);
						joinEntity.setToField(new String[] { idField.getName() });
						joinEntity.setToTableAlias("parent");
						joinClassList.add(joinEntity);
					}
				}
			}
		}
		if (joinClassList != null && joinClassList.size() > 1) {
			Collections.sort(joinClassList);
		}
	}

	protected String[] buildJoinSql(String mainAlias) {
		List<Field> fields = EntityUtils.findTableColumn(this.clazz);
		StringBuilder fieldStr = new StringBuilder();
		if (this.idField != null) {
			Column column = idField.getAnnotation(Column.class);
			String name = idField.getName();
			if (column != null && StringUtils.isNotBlank(column.name())) {
				name = column.name();
			}
			if (StringUtils.isNotBlank(mainAlias)) {
				fieldStr.append(mainAlias).append(".").append(name);
			} else {
				fieldStr.append(name);
			}
		}
		for (int i = 0; i < fields.size(); i++) {
			Field field = fields.get(i);
			Column column = field.getAnnotation(Column.class);
			if (column != null) {
				if (idField != null && field.getName().equals(idField.getName())) {
					continue;
				}
				String name = column.name();
				if (StringUtils.isBlank(name)) {
					name = field.getName();
				}
				if (fieldStr.length() > 0) {
					fieldStr.append(",");
				}

				if (StringUtils.isNotBlank(mainAlias)) {
					fieldStr.append(mainAlias).append(".").append(name);
				} else {
					fieldStr.append(name);
				}
			}
		}
		StringBuilder sql = new StringBuilder();
		if (this.joinClassList != null) {
			// 处理多个相同表连接
			List<String> aliasList = new ArrayList<String>();
			for (JoinEntity joinEntity : this.joinClassList) {
				String alias = joinEntity.getToTableAlias();
				if (StringUtils.isNotBlank(joinEntity.getName())) {
					fieldStr.append(",").append(alias).append(".").append(joinEntity.getName()).append(" AS ").append(joinEntity.getFieldName());
				} else {
					List<Field> childFields = EntityUtils.findTableColumn(joinEntity.getClazz());
					for (int i = 0; i < childFields.size(); i++) {
						Field field = childFields.get(i);
						Column column = field.getAnnotation(Column.class);
						if (column != null) {
							String name = column.name();
							if (StringUtils.isBlank(name)) {
								name = field.getName();
							}
							fieldStr.append(",").append(alias).append(".").append(name);
						} else {
							Id id = field.getAnnotation(Id.class);
							if (id != null) {
								fieldStr.append(",").append(alias).append(".").append(field.getName());
							}
						}
					}
				}
				// 相同的表连接只能一次
				if (aliasList.contains(alias)) {
					continue;
				}
				
				String fromFieldAlias = "";
				if (!joinEntity.getFromField()[0].contains(".") && StringUtils.isNotBlank(mainAlias)) {
					fromFieldAlias = mainAlias + ".";
				}
				StringBuilder temp = new StringBuilder(joinEntity.getType() + " JOIN " + joinEntity.getToTable() + " " + alias + " ON " + alias + "."
						+ joinEntity.getToField()[0] + " = " + fromFieldAlias + joinEntity.getFromField()[0]);
				for (int i = 1; i < joinEntity.getFromField().length && i < joinEntity.getToField().length; i++) {
					fromFieldAlias = "";
					if (!joinEntity.getFromField()[i].contains(".") && StringUtils.isNotBlank(mainAlias)) {
						fromFieldAlias = mainAlias + ".";
					}
					temp.append(" AND ").append(alias).append(".").append(joinEntity.getToField()[i]).append(" = ").append(fromFieldAlias)
							.append(joinEntity.getFromField()[i]);
				}
				sql.append(" ").append(temp);
				aliasList.add(alias);
			}
		}
		return new String[] { fieldStr.toString(), sql.toString() };
	}

	protected String[] buildJoinSql() {
		return buildJoinSql("t");
	}

	public Conditions buildConditions(T entity) {
		return Conditions.build(entity);
	}

}
